#include "stm32f10x.h"
#include "DMA_UART1.h"
#include <stdio.h>

static uint8_t tx_data[50];
//static uint16_t tx_data_length;


/*

    自定义的帧协议
    0x5d 为 帧开始标志
    一字节代表数据包的原始长度（单位：字节），不包含帧头和帧尾，也不包含长度字段
    帧的有效载荷为 4 字节
    0xf7 为 帧结束标志
    
    帧长度固定为 6 字节
*/



// 接收到的原始的字节帧数据
static uint8_t rx_raw_byte_data[FRAME_LENGTH];

/*
    从帧中提取出来的原始数据包
*/
uint8_t data_package[MAX_DATA_PACKAGE_LENGTH];

void init_uart1_tx_dma(void);

void init_uart1_rx_dma(void);

void init_dma_for_uart1(void){

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE );

    
    init_uart1_tx_dma();
    
    init_uart1_rx_dma();
}

/**
    初始化 uart1 tx 口 的dma
*/
void init_uart1_tx_dma(void){
    

    DMA_InitTypeDef dma_init;
    

    
    // 发送数据时：uart 发送寄存器为目的地址
    dma_init.DMA_DIR=DMA_DIR_PeripheralDST;
    
    dma_init.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    dma_init.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
    
    dma_init.DMA_MemoryBaseAddr = (uint32_t)tx_data;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    
    dma_init.DMA_Priority = DMA_Priority_Low;
    dma_init.DMA_BufferSize=0;
    dma_init.DMA_M2M = DMA_M2M_Disable;
    
    //重点： 不能开启循环模式，否则会重复不停地往串口发送数据
    dma_init.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Circular;
    
    DMA_Init(DMA1_Channel4,&dma_init);
    
    /*
    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
    
    NVIC_InitTypeDef nvic;
    nvic.NVIC_IRQChannel=DMA1_Channel4_IRQn;
    nvic.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&nvic);*/
}

uint8_t * getBuffer(void){
    // 如果有数据尚未传输完成就循环等待 (||DMA_GetCurrDataCounter(DMA1_Channel4)>0
    //while(DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
    
    DMA_Cmd(DMA1_Channel4,DISABLE);
    
    return tx_data;
}

void send(uint16_t length){

    // 如果有数据尚未传输完成就循环等待 (||DMA_GetCurrDataCounter(DMA1_Channel4)>0
    //while(DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
    
    DMA_Cmd(DMA1_Channel4,DISABLE);
    
    //tx_data_length = length;
    
    DMA_SetCurrDataCounter(DMA1_Channel4,length);
    DMA_Cmd(DMA1_Channel4,ENABLE);
    
    

}

void DMA1_Channel4_IRQHandler(void){
   if(DMA_GetITStatus(DMA1_IT_TC4)==SET){
    
        
        DMA_ClearITPendingBit(DMA1_IT_TC4);
        
    }
    
}


void init_uart1_rx_dma(void){

    
    DMA_InitTypeDef dma_init;
    
    
    
    // 接收数据时：uart 发送寄存器为源地址
    dma_init.DMA_DIR=DMA_DIR_PeripheralSRC;
    
    dma_init.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    dma_init.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
    
    dma_init.DMA_MemoryBaseAddr = (uint32_t)rx_raw_byte_data;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    
    dma_init.DMA_Priority = DMA_Priority_Low;
    dma_init.DMA_BufferSize= FRAME_LENGTH;
    dma_init.DMA_M2M = DMA_M2M_Disable;
    
    /** 启用循环模式
    
        每当串口接收到一字节数据之后就会自动触发DMA转运
        如果不启用 循环模式
        那么每一次DMA结束之后，都需要手动关闭DMA，设置计数器，重新启用DMA（虽然没有测试过，但是过程应该是这样）
    */
    dma_init.DMA_Mode = DMA_Mode_Circular;
    
    DMA_Init(DMA1_Channel5,&dma_init);


    DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
    
    NVIC_InitTypeDef nvic;
    nvic.NVIC_IRQChannel=DMA1_Channel5_IRQn;
    nvic.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&nvic);
    
    // 启动 DMA
    DMA_Cmd(DMA1_Channel5,ENABLE);
}
/**
    去除帧相关数据之后，原始数据包的长度
*/
static uint8_t raw_data_length;

static uint8_t receiving_status = FRAME_START_STATUS;

uint8_t find_start_byte(uint8_t byte);
uint8_t build_payload(uint8_t byte);

/**
解析到当前数据包的第几字节
仅仅在 FRAME_PAYLOAD_STATUS 状态下有效
*/
static uint8_t current_data_package_position;

/**
DMA 中断函数中 解析帧中的数据包
*/
void DMA1_Channel5_IRQHandler(void){

    if(DMA_GetITStatus(DMA1_IT_TC5)==SET){
        
        for(uint8_t i=0;i<FRAME_LENGTH;i++){
            
            uint8_t byte = rx_raw_byte_data[i];
            
            switch(receiving_status){
                
                case FRAME_START_STATUS:
                    receiving_status = find_start_byte(byte );
                    break;
                case FRAME_LENGTH_STATUS:
                    if(current_data_package_position==0){
                        
                        raw_data_length = byte;
                    }else{
                        // 上一帧 中含有尚未解析完的数据包
                    }
                    
                    receiving_status = FRAME_PAYLOAD_STATUS;
                    break;
                case FRAME_PAYLOAD_STATUS:
                    receiving_status = build_payload(byte);
                    break;
                case FRAME_END_STATUS:
                    receiving_status = FRAME_END_BYTE == byte ? FRAME_START_STATUS:FRAME_END_BYTE;
                    break;
            }
        }
        
        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }
}
/**
找到帧的起始数据包
*/
uint8_t find_start_byte(uint8_t byte){

    if(byte==FRAME_START_BYTE){
        return FRAME_LENGTH_STATUS;
    }
    return FRAME_START_STATUS;
}



/**
当前解析到帧中的第几个有效载荷
*/
static uint8_t current_frame_payload_position;

/*
    标记已经有数据包被成功的解析
 如果置为1，代表已经从串口接收数据并从帧中提取原始数据包完成

*/
static uint8_t data_package_received;

/**
    解析原始数据包
*/
void build_raw_data_package(uint8_t byte){
    data_package_received=0;
    
    data_package[current_data_package_position] = byte;
    current_data_package_position++;
    
    if(current_data_package_position==raw_data_length){
        // 当前的原始数据包已经被完整解析
        
        // 为了能正常的把数据包显示为字符串（字符数组）
        // 在数据包的最后一位设置为空字符 \0
        data_package[current_data_package_position]=0;
        // 如果有操作系统的话，在这里把因为等待数据包而被阻塞的线程给恢复运行
        data_package_received=1;
        current_data_package_position=0;
    }
}

/**
解析帧中的有效载荷，还原原始数据包
*/
uint8_t  build_payload(uint8_t byte){
    
    build_raw_data_package(byte);
    
    current_frame_payload_position++;
    
    if(current_frame_payload_position == FRAME_LENGTH-3){
        // 抵达当前帧的结束位置 
        current_frame_payload_position=0;
        return FRAME_END_STATUS;
    }
    
    return FRAME_PAYLOAD_STATUS;
}

uint8_t is_receive_successful(void){

    return data_package_received;
}

uint8_t data_package_length(){
    return raw_data_length;
}

void send_format(char * string, char * format){

    uint8_t * str  = getBuffer();

    uint16_t length = sprintf((char *)str,string,format);
    
    send(length);
}
